import {  _decorator, Camera, Color, Component, Director, director, gfx, Layers, Node, renderer, RenderTexture, Sprite, SpriteFrame, UITransform, Widget } from "cc";

const { ccclass, property, requireComponent, executeInEditMode } = _decorator;

function setNodeAndChildrenLayer(node: Node, layer: number) {
    node.layer = layer;
    for (let i = 0; i < node.children.length; i++) {
        const child = node.children[i];
        setNodeAndChildrenLayer(child, layer);
    }
}

@ccclass("UINodeRender")
@requireComponent(UITransform)
// @executeInEditMode(false)
export class UINodeRender extends Component {
    @property(Sprite)
    private targetSprite: Sprite = null;

    @property(Camera)
    private camera: Camera = null;

    private _renderTexture: RenderTexture = null;
    private _spriteFrame: SpriteFrame = null;

    private _layer = -1;

    public get spriteFrame(): SpriteFrame {
        return this._spriteFrame;
    }

    public get renderTexture(): RenderTexture {
        return this._renderTexture;
    }

    private createRenderTexture(): void {
        if (!this._renderTexture) {
            const uiTransform = this.getComponent(UITransform);
            const width = uiTransform.width;
            const height = uiTransform.height;
            this._renderTexture = new RenderTexture();
            this._renderTexture.reset({ width, height });
        }
        if (!this._spriteFrame) {
            this._spriteFrame = new SpriteFrame(`UINodeRender-${this.node.uuid}`);
            this._spriteFrame.texture = this._renderTexture;
        }
    }

    private calculateLayer(): void {
        if (this._layer !== -1) return;
        const layer = Layers.nameToLayer("UI_RENDER_TEXTURE");
        if (layer === 0) {
            console.error("UINodeRender: UI_RENDER_TEXTURE layer not found");
        }
        this._layer = 1 << layer;
        setNodeAndChildrenLayer(this.node, this._layer);
    }

    private createCamera(): void {
        if (this.camera) return;

        const cameraNode = new Node("UINodeRenderCamera");
        cameraNode.layer = this._layer;
        this.node.addChild(cameraNode);
        const widget = cameraNode.addComponent(Widget);
        widget.verticalCenter = 0;
        widget.horizontalCenter = 0;
        widget.isAlignVerticalCenter = true;
        widget.isAlignHorizontalCenter = true;
        cameraNode.z = 5;

        this.camera = cameraNode.addComponent(Camera);
        this.camera.clearFlags = gfx.ClearFlagBit.COLOR;
        this.camera.clearColor = new Color(0, 0, 0, 0);
        this.camera.visibility = Layers.makeMaskInclude([this._layer]);
        this.camera.projection = renderer.scene.CameraProjection.ORTHO;
        this.camera.clearStencil = 0;
        this.camera.orthoHeight = this._renderTexture.height / 2;
        this.camera.near = 0.1;
        this.camera.far = 10;
    }

    private init(): void {
        this.createRenderTexture();
        this.calculateLayer();
        this.createCamera();
        this.camera.targetTexture = this._renderTexture;
    }

    protected onLoad(): void {
        this.init();

        if (this.targetSprite) {
            this.targetSprite.spriteFrame = this._spriteFrame;
        }
    }

    public enableRender(): void {
        if (!this.camera) return;
        this.camera.enabled = true;
    }

    public disableRender(): void {
        if (!this.camera) return;
        this.camera.enabled = false;
    }

    public async renderer(): Promise<void> {
        this.init();
        return new Promise((resolve)=>{
            director.once(Director.EVENT_BEFORE_DRAW, ()=>{
                director.root.pipeline.render([this.camera.camera]);
                resolve();
            });
        });
    }

    public resetUILayer(): void {
        setNodeAndChildrenLayer(this.node, Layers.Enum.UI_2D);
    }

    public destroyRender(): void {
        this.resetUILayer();
        if (this.camera) {
            this.camera.node.destroy();
            this.camera = null;
        }
        if (this._spriteFrame) {
            this._spriteFrame.destroy();
            this._spriteFrame = null;
        }
        if (this._renderTexture) {
            this._renderTexture.destroy();
            this._renderTexture = null;
        }
        this.destroy();
    }
}